package com.bite.friend.service.user.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bit.common.security.handler.ServiceException;
import com.bit.common.security.service.TokenService;
import com.bite.common.core.contants.CacheConstants;
import com.bite.common.core.contants.Constants;
import com.bite.common.core.contants.HttpConstants;
import com.bite.common.core.domain.LoginUser;
import com.bite.common.core.domain.R;
import com.bite.common.core.domain.vo.LoginUserVO;
import com.bite.common.core.enums.ResultCode;
import com.bite.common.core.enums.UserIdentity;
import com.bite.common.core.enums.UserStatus;
import com.bite.common.core.utils.ThreadLocalUtil;
import com.bite.common.message.service.AliSmsService;
import com.bite.friend.domain.user.User;
import com.bite.friend.domain.user.dto.UserUpdateDTO;
import com.bite.friend.domain.user.dto.UserDTO;
import com.bite.friend.domain.user.vo.UserVO;
import com.bite.friend.manager.UserCacheManager;
import com.bite.friend.mapper.user.UserToMapper;
import com.bite.friend.service.user.IUserService;
import com.bite.redis.config.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
@Slf4j
public class UserServiceToImpl implements IUserService {

    @Autowired
    private UserToMapper userMapper;

    @Autowired
    private AliSmsService aliSmsService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private UserCacheManager userCacheManager;


    //获取次数
    @Value("${sms.send-limit:3}")
    private Integer sendLimit;

    //有效时间
    @Value("${sms.code-expiration:5}")
    private Long phoneCodeExpiration;

    @Value(("${jwt.secret}"))
    private String secret;

    //开关
    @Value("${sms.is-send:true}")
    private boolean isSend;

    @Value("${file.oss.downloadUrl}")
    private String downloadUrl;

    @Override
    public boolean sendCode(UserDTO userDTO) {
        if (!checkPhone(userDTO.getPhone())){
            throw new ServiceException(ResultCode.FAILED_USER_PHONE);
        }
        //操作频繁判断  每隔一分钟才能获取
        String phoneCodeKey = getPhoneCodeKey(userDTO.getPhone());  //获取验证码的key
        //获取剩余时间
        Long expire = redisService.getExpire(phoneCodeKey, TimeUnit.SECONDS);
        if (expire != null && (phoneCodeExpiration * 60 - expire)< 60){
            throw new ServiceException(ResultCode.FAILED_FREQUENT);
        }

        //对每天的获取次数进行限制, 3次,第二天,清零  计数
        //计数:String类型  key :c:t:手机号
        //获取已经请求的次数,和50对比--放nacos   大于限制,抛出异常 不大于,继续执行,并且获取计数+1
        String timeCodeKey = getTimeCodeKey(userDTO.getPhone());  //验证码次数的key
        //判断
        Long sendTimes = redisService.getCacheObject(timeCodeKey, Long.class);
        if (sendTimes != null && sendTimes >= sendLimit){
            throw new ServiceException(ResultCode.FAILED_TIME_LIMIT);
        }

        //验证码生成  hutool给了一个方法
        //开关打开,随机验证码  开关关闭,固定验证码
        String code = isSend?RandomUtil.randomNumbers(6): Constants.DEFAULT_CODE;


        //验证码在后台需不需要存储--存储在redis
        //String key: p:code:手机号(区分)  value:code
        redisService.setCacheObject(phoneCodeKey,code,phoneCodeExpiration, TimeUnit.MINUTES);
        //开关控制是否发送
        if (isSend){
            boolean sendMobileCode = aliSmsService.sendMobileCode(userDTO.getPhone(), code);
            if (!sendMobileCode){
                throw new ServiceException(ResultCode.FAILED_SEND_CODE);
            }
        }
        redisService.increment(timeCodeKey);

        //每次获取次数情况,进行每日清空
        if (sendTimes == null){  //当天第一次发起发起获取验证码的请求
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            //设置时间
            redisService.expire(timeCodeKey,seconds,TimeUnit.SECONDS);
        }
        return true;
    }


    /**
     * 登录/注册
     * @param phone
     * @param code
     */
    @Override
    public String codeLogin(String phone, String code) {
        //新老用户先完成验证码比对,这样注册就只需比对一次
        checkCode(phone, code);

        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));
        //判断新老用户
        //新用户
        if (user == null){
            //注册逻辑
            //验证码比对
            user = new User();
            user.setPhone(phone);
            user.setStatus(UserStatus.NORMAL.getValues());
            user.setCreateBy(1L);
            userMapper.insert(user);
        }
        //生成token
        return tokenService.createToken(user.getUserId(),secret, UserIdentity.ORDINARY.getValue(),user.getNickName(),user.getHeadImage());

    }

    @Override
    public boolean logout(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        return tokenService.deleteLoginUser(token,secret);
    }

    /**
     * 验证码比对
     * @param phone
     * @param code
     * @return
     */
    private void checkCode(String phone, String code) {
        //获取生成验证码
        String phoneCodeKey = getPhoneCodeKey(phone);
        String cacheCode = redisService.getCacheObject(phoneCodeKey, String.class);
        //比较
        //判断验证码是否过期
        if (StrUtil.isEmpty(cacheCode)){
            throw new ServiceException(ResultCode.FAILED_INVALID_code);
        }
        //获取到验证码,比较
        if (!cacheCode.equals(code)){
            throw new ServiceException(ResultCode.FAILED_ERROR_code);
        }
        //验证码正确
        //先把redis中生成的验证码删除,它的使命已经完成
        redisService.deleteObject(phoneCodeKey);
    }

    /**
     * redis中存储的key
     * @param phone
     * @return
     */
    private String getPhoneCodeKey(String phone) {
        return CacheConstants.PHONE_CODE_KEY+phone;
    }

    //参数合法性判断 是不是一个手机号
    public static boolean checkPhone(String phone) {
        //正则表达式编译验证码格式
        Pattern regex = Pattern.compile("^1[2|3|4|5|6|7|8|9][0-9]\\d{8}$");
        //创建Matcher对象
        Matcher m = regex.matcher(phone);
        //检查整个字符串phone是否完全匹配正则表达式
        return m.matches();
    }

    /**
     * 获取验证码次数中存储的key
     * @param phone
     * @return
     */
    private String getTimeCodeKey(String phone) {
        return CacheConstants.CODE_TIME_KEY+phone;
    }

    /**
     * 获取用户当前信息-查看管理员昵称
     * @param token
     * @return
     */
    @Override
    public R<LoginUserVO> info(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        LoginUser loginUser = tokenService.getLoginUser(token, secret);
        if (loginUser == null) {
            return R.fail();
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setNickName(loginUser.getNickName());
        if (StrUtil.isNotEmpty(loginUser.getHeadImage())){
            loginUserVO.setHeadImage(downloadUrl + loginUser.getHeadImage());
        }

        return R.ok(loginUserVO);
    }


    /**
     * 获取用户个人信息
     * @return
     */
    @Override
    public UserVO detail() {
        Long userId  = ThreadLocalUtil.get(Constants.USER_ID,Long.class);
        if (userId == null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        //从缓存中查
        UserVO userVO = userCacheManager.getUserById(userId);
        if (userVO == null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        if (StrUtil.isNotEmpty(userVO.getHeadImage())){
            userVO.setHeadImage(downloadUrl + userVO.getHeadImage());
        }
        return userVO;
    }

    @Override
    public int edit(UserUpdateDTO userUpdateDTO) {
        //查询用户是否存在
        //对用户信息进行修改
        Long userId = ThreadLocalUtil.get(Constants.USER_ID,Long.class);
        if (userId == null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        //从数据库中查是否有信息
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setNickName(userUpdateDTO.getNickName());
        user.setSex(userUpdateDTO.getSex());
        user.setSchoolName(userUpdateDTO.getSchoolName());
        user.setMajorName(userUpdateDTO.getMajorName());
        user.setPhone(userUpdateDTO.getPhone());
        user.setEmail(userUpdateDTO.getEmail());
        user.setWechat(userUpdateDTO.getWechat());
        user.setIntroduce(userUpdateDTO.getIntroduce());
        //更新用户个人信息接口
        userCacheManager.refreshUser(user);
        //更新用户登录接口
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));
        //写入数据库

        return userMapper.updateById(user);
    }

    @Override
    public int updateHeadImage(String headImage) {
        //上传后,文件名会返回给前端
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setHeadImage(headImage);
        //更新用户缓存
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));
        return userMapper.updateById(user);
    }
}